/// <reference path="../disco/disco.model.ts" />
/// <reference path="../disco/disco.model.helper.ts" />
/// <reference path="../disco/disco.web.ts" />
var disco;
(function (disco) {
    var Ontology = (function () {
        function Ontology(serviceUri) {
            this.serviceUri = serviceUri;
        }
        Ontology.prototype.isInitialized = function () {
            return this.dataContext != null;
        };

        Ontology.prototype.init = function (success, error) {
            var _this = this;
            disco.web.Requests.loadOntologyMetadata(this.serviceUri + "$metadata", function (metadata) {
                var context = new disco.model.DataContext();
                var contextMgr = new disco.model.helper.DataContextManager(context);

                context.serviceUri = _this.serviceUri;
                context.metadata = metadata;
                contextMgr.generateEntityModel();

                _this.dataContext = context;
                success(context);
            }, error);
        };

        Ontology.prototype.getEntityById = function (type, id, success, error) {
            var set = this.dataContext.entitySet(type.name);

            var existing = disco.Collections.nullableSingle(set.rows(), function (e) {
                return e.property('Id').value == id;
            });
            if (existing != null)
                success(existing);
else
                this.getEntityByPath(set.name + '(' + id + ')', success, error);
        };

        Ontology.prototype.getEntityByPath = function (path, success, error) {
            var _this = this;
            disco.web.Requests.loadOntologyContent(this.serviceUri + path, function (data) {
                success(_this.registerFromRawEntity(data));
            }, error);
        };

        Ontology.prototype.getEntitiesByPath = function (path, success, error) {
            this.getEntitiesByUrl(this.serviceUri + path, success, error);
        };

        Ontology.prototype.getEntitiesByUrl = function (url, success, error) {
            var _this = this;
            disco.web.Requests.loadOntologyContent(url, function (data) {
                var rawEntities = data.value || [data];
                var entities = [];

                rawEntities.forEach(function (rawEntity) {
                    entities.push(_this.registerFromRawEntity(rawEntity));
                });

                success(entities);
            }, error);
        };

        Ontology.prototype.registerFromRawEntity = function (rawEntity) {
            var entityMgr = new disco.model.helper.EntityManager(this.dataContext);
            var entity = entityMgr.fromRawEntity(rawEntity);

            var en = disco.Collections.nullableSingle(entity.type.entitySet.rows(), function (e) {
                return e.property('Id').value == entity.property('Id').value;
            });
            if (en)
                return en;

            var setMgr = new disco.model.helper.EntitySetManager();
            setMgr.set(this.dataContext.entitySet(entity.type.name));

            setMgr.add(entity);
            return entity;
        };

        Ontology.prototype.getEntitiesByTemplate = function (template, success, url) {
            if (typeof url === "undefined") { url = null; }
            var mode = (url == null) ? 'entitySet' : 'url';

            var constraints = {};
            var templates = {};
            var computedUrl;

            Object.keys(template.properties || {}).forEach(function (key) {
                var val = template.properties[key];

                if ((typeof val) == 'object' && disco.Type.get(val) != '[object Array]')
                    templates[key] = val;
else
                    constraints[key] = val;
            });

            if (mode == 'entitySet') {
                var filterPath = '?$filter=true';
                Object.keys(constraints).forEach(function (key) {
                    var val = constraints[key];
                    var str = val;
                    if (disco.Type.get(val) != '[object Array]')
                        val = [val];
                    str = '(' + disco.Collections.implode(val.map(function (v) {
                        return key + ' eq ' + v;
                    }), ' or ') + ')';
                    filterPath += ' and ' + str;
                });
                computedUrl = this.serviceUri + (template.type).entitySet.name + filterPath;
            } else if (mode == 'url') {
                computedUrl = url;
            }

            var self = this;
            this.getEntitiesByUrl(computedUrl, function (entities) {
                var entityCallbacks = entities.map(function (entity) {
                    return (function (onEntityReady) {
                        var propertyCallbacks = Object.keys(templates).map(function (key) {
                            return (function (onPropertyReady) {
                                var property = entity.property(key);
                                var template = templates[key];
                                if (!property.property.isNavigation || property.value) {
                                    onPropertyReady();
                                    return;
                                }
                                self.getEntitiesByTemplate(template, function (propertyValue) {
                                    if (property.property.isMany)
                                        property.value = propertyValue;
else if (propertyValue.length > 0)
                                        property.value = propertyValue[0];
                                    onPropertyReady();
                                }, property.url);
                            });
                        });
                        disco.Callbacks.batch(propertyCallbacks, onEntityReady);
                    });
                });
                disco.Callbacks.batch(entityCallbacks, function () {
                    success(entities);
                });
            }, function (error) {
            });
        };

        //example: entityTemplate = { type: <Content>, properties: { Title: "Title", Text: "Text } }
        Ontology.prototype.addEntity = function (entityTemplate, success, error) {
            var _this = this;
            disco.web.Requests.addEntity(this.dataContext.serviceUri + entityTemplate.type.entitySet.name, entityTemplate.properties || {}, function (rawEntity) {
                success(_this.registerFromRawEntity(rawEntity));
            }, function (err) {
                error(err);
            });
        };

        Ontology.prototype.removeEntity = function (entityType, entityId, success, error) {
            this.removeEntityByPath(entityType.entitySet.name + '(' + entityId + ')', success, error);
        };

        Ontology.prototype.removeEntityByPath = function (path, success, error) {
            this.removeEntityByUrl(this.dataContext.serviceUri + path, success, error);
        };

        Ontology.prototype.removeEntityByUrl = function (url, success, error) {
            disco.web.Requests.removeEntity(url, function (d) {
                success();
            }, function (e) {
                error(e);
            });
        };

        Ontology.prototype.addConnection = function (entityType, entityId, property, valueId, success, error) {
            if (!property.isNavigation)
                throw new Error('disco.Ontology.addConnection: operation is only valid for navigation properties.');
            disco.web.Requests.addEntityConnection(this.dataContext.serviceUri + entityType.entitySet.name + '(' + entityId + ')', property.name, this.dataContext.serviceUri + property.type.entitySet.name + '(' + valueId + ')', success, error);
        };

        Ontology.prototype.removeConnection = function (entityType, entityId, property, valueId, success, error) {
            if (!property.isNavigation)
                throw new Error('disco.Ontology.addConnection: operation is only valid for navigation properties.');
            disco.web.Requests.removeEntityConnection(this.dataContext.serviceUri + entityType.entitySet.name + '(' + entityId + ')', property.name + '(' + valueId + ')', success, error);
        };

        Ontology.prototype.clearCache = function () {
            var _this = this;
            Object.keys(this.dataContext.entitySets).forEach(function (key) {
                var set = _this.dataContext.entitySets[key];
                set.rows([]);
            });
        };
        return Ontology;
    })();
    disco.Ontology = Ontology;
})(disco || (disco = {}));
